﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenMvvmToolkit.Attributes;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.ViewModels;
using MugenMvvmToolkitTest.TestInfrastructure;
using MugenMvvmToolkitTest.TestModels;
using Should;
using System.Linq;

namespace MugenMvvmToolkitTest.Infrastructure
{
    public class MappingViewModel : ViewModelBase { }

    [PageViewModel(typeof(MappingViewModel))]
    public class PageMappingView : TestView
    {
    }

    [PageViewModel(typeof(MappingViewModel), ViewMappingProviderTestBase.TestUrl, UriKind = UriKind.Absolute, Name = ViewMappingProviderTestBase.VmName)]
    public class PageMappingWithUrlView : TestView
    {
    }

    [ViewModel(typeof(MappingViewModel))]
    public class ViewModelMapView : TestView
    {

    }

    [ViewModel(typeof(MappingViewModel), Name = ViewMappingProviderTestBase.VmName)]
    public class ViewModelMapWithNameView : TestView
    {

    }

    [ViewModel(typeof(GridViewModel<>))]
    public class GenericTestView : TestView { }

    [PageViewModel(typeof(GridViewModel<>), ViewMappingProviderTestBase.TestUrl, UriKind = UriKind.Absolute)]
    public class GenericPageTestView : TestView { }

    [TestClass]
    public class ViewMappingProviderTestBase : TestBase
    {
        #region Fields

        public const string TestUrl = "http://test.com/";
        public const string VmName = "VmName";

        #endregion

        #region Test methods

        [TestMethod]
        public void VmProviderShouldReturnMappingWithoutName()
        {
            var assemblies = new[]
            {
#if !NETFX_CORE
                typeof (ViewMappingProviderTestBase).Assembly
#else
                                 typeof(ViewMappingProviderTestBase).GetTypeInfo().Assembly
#endif

            };

            IViewMappingProvider viewMappingProvider = GetViewMappingProvider(assemblies);
            var viewMap = viewMappingProvider.FindMappingForViewModel(typeof(MappingViewModel), null, true);
            viewMap.Name.ShouldBeNull();
            viewMap.ViewModelType.ShouldEqual(typeof(MappingViewModel));
            viewMap.ViewType.ShouldEqual(typeof(ViewModelMapView));
        }

        [TestMethod]
        public void VmProviderShouldReturnMappingWithName()
        {
            var assemblies = new[]
            {
#if !NETFX_CORE
                typeof (ViewMappingProviderTestBase).Assembly
#else
                                 typeof(ViewMappingProviderTestBase).GetTypeInfo().Assembly
#endif

            };

            IViewMappingProvider viewMappingProvider = GetViewMappingProvider(assemblies);
            var viewMap = viewMappingProvider.FindMappingForViewModel(typeof(MappingViewModel), VmName, true);
            viewMap.Name.ShouldEqual(VmName);
            viewMap.ViewType.ShouldEqual(typeof(ViewModelMapWithNameView));
            viewMap.ViewModelType.ShouldEqual(typeof(MappingViewModel));
        }

        [TestMethod]
        public void PageProviderShouldReturnMappingWithoutName()
        {
            var assemblies = new[]
            {
#if !NETFX_CORE
                typeof (ViewMappingProviderTestBase).Assembly
#else
                                     typeof(ViewMappingProviderTestBase).GetTypeInfo().Assembly
#endif
            };
            var viewMappingProvider = GetViewPageMappingProvider(assemblies);

#if !NETFX_CORE
            var assemblyName = assemblies[0].FullName;
#else
            var assemblyName = assemblies[0].FullName;
#endif
            IViewPageMappingItem viewMap = viewMappingProvider.FindMappingForView(typeof(PageMappingView), true);
            viewMap.Name.ShouldBeNull();
            viewMap.Uri.ToString()
                .ShouldEqual(string.Format("/{0};componentMugenMvvmToolkitTest/Infrastructure/PageMappingView.xaml",
                    new AssemblyName(assemblyName).Name));
            viewMap.ViewModelType.ShouldEqual(typeof(MappingViewModel));
            viewMap.ViewType.ShouldEqual(typeof(PageMappingView));
        }

        [TestMethod]
        public void PageProviderShouldReturnMappingWithName()
        {
            var assemblies = new[]
            {
#if !NETFX_CORE
                typeof (ViewMappingProviderTestBase).Assembly
#else
                                     typeof(ViewMappingProviderTestBase).GetTypeInfo().Assembly
#endif
            };
            var viewMappingProvider = GetViewPageMappingProvider(assemblies);

#if !NETFX_CORE
            var assemblyName = assemblies[0].FullName;
#else
            var assemblyName = assemblies[0].FullName;
#endif
            var viewMap = viewMappingProvider.FindMappingForViewModel(typeof(MappingViewModel), VmName, true);
            viewMap.Name.ShouldEqual(VmName);
            viewMap.Uri.ToString().ShouldEqual(TestUrl);
            viewMap.ViewModelType.ShouldEqual(typeof(MappingViewModel));
            viewMap.ViewType.ShouldEqual(typeof(PageMappingWithUrlView));
        }

        [TestMethod]
        public void VmProviderShouldResolveGenericVm()
        {
            var assemblies = new[]
            {
#if !NETFX_CORE
                typeof (ViewMappingProviderTestBase).Assembly
#else
                                 typeof(ViewMappingProviderTestBase).GetTypeInfo().Assembly
#endif

            };

            IViewMappingProvider viewMappingProvider = GetViewMappingProvider(assemblies);

            IViewMappingItem mappingItem = viewMappingProvider.FindMappingForViewModel(typeof(GridViewModel<object>), null, true);
            mappingItem.ViewModelType.ShouldEqual(typeof(GridViewModel<>));
            mappingItem.ViewType.ShouldEqual(typeof(GenericTestView));
            mappingItem.Name.ShouldBeNull();
        }

        [TestMethod]
        public void PageProviderShouldResolveGenericVm()
        {
            var assemblies = new[]
            {
#if !NETFX_CORE
                typeof (ViewMappingProviderTestBase).Assembly
#else
                                 typeof(ViewMappingProviderTestBase).GetTypeInfo().Assembly
#endif

            };

            var viewMappingProvider = GetViewPageMappingProvider(assemblies);

            var mappingItem = viewMappingProvider.FindMappingForViewModel(typeof(GridViewModel<object>), null, true);
            mappingItem.ViewModelType.ShouldEqual(typeof(GridViewModel<>));
            mappingItem.ViewType.ShouldEqual(typeof(GenericPageTestView));
            mappingItem.Name.ShouldBeNull();
            mappingItem.Uri.ToString().ShouldEqual(TestUrl);
        }

        [TestMethod]
        public void PageProviderShouldThrowExceptionIfMappingNotFoundUsingViewTrue()
        {
            var provider = GetViewPageMappingProvider(new Assembly[0]);
            ShouldThrow<InvalidOperationException>(() => provider.FindMappingForView(typeof(IView), true));
        }

        [TestMethod]
        public void PageProviderShouldNotThrowExceptionIfMappingNotFoundUsingViewFalse()
        {
            var provider = GetViewPageMappingProvider(new Assembly[0]);
            provider.FindMappingForView(typeof(IView), false).ShouldBeNull();
        }

        [TestMethod]
        public void PageProviderShouldThrowExceptionIfMappingNotFoundUsingViewModelTrue()
        {
            var provider = GetViewPageMappingProvider(new Assembly[0]);
            ShouldThrow<InvalidOperationException>(() => provider.FindMappingForViewModel(typeof(IViewModel), null, true));
        }

        [TestMethod]
        public void PageProviderShouldNotThrowExceptionIfMappingNotFoundUsingViewModelFalse()
        {
            var provider = GetViewPageMappingProvider(new Assembly[0]);
            provider.FindMappingForViewModel(typeof(IViewModel), null, false).ShouldBeNull();
        }

        [TestMethod]
        public void VmProviderShouldThrowExceptionIfMappingNotFoundUsingViewModelTrue()
        {
            var provider = GetViewMappingProvider(new Assembly[0]);
            ShouldThrow<InvalidOperationException>(() => provider.FindMappingForViewModel(typeof(IViewModel), null, true));
        }

        [TestMethod]
        public void VmProviderShouldNotThrowExceptionIfMappingNotFoundUsingViewModelFalse()
        {
            var provider = GetViewMappingProvider(new Assembly[0]);
            provider.FindMappingForViewModel(typeof(IViewModel), null, false).ShouldBeNull();
        }

        #endregion

        #region Methods

        protected virtual IViewMappingProvider GetViewMappingProvider(IList<Assembly> assemblies)
        {
            return new AttributeViewMappingProvider(assemblies);
        }

        protected virtual IViewPageMappingProvider GetViewPageMappingProvider(IList<Assembly> assemblies)
        {
            return new AttributeViewPageMappingProvider(assemblies);
        }

        #endregion

    }
}